package com.gao.util;

import com.gao.annontation.StarAuthenticated;
import com.gao.annontation.StarAuthority;
import lombok.Data;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.PostConstruct;
import javax.servlet.Servlet;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author gao
 * @date 2022/12/05 11:04:44
 */

@Data
@Slf4j
@Component
@ConditionalOnClass({Servlet.class, DispatcherServlet.class})
@RequiredArgsConstructor
public class AuthenticationRedisUtils {

    @NonNull
    private WebApplicationContext applicationContext;
    @NonNull
    private RedisTemplate redisTemplate;

    @Value("${spring.application.path-prefix}")
    private String pathPrefix;

    private List<String> publicUrls = new ArrayList<>();
    private List<String> authUrls = new ArrayList<>();

    @PostConstruct
    public void init() {
        // 获取所有端点
        RequestMappingHandlerMapping handlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();

        // 每次重启当前应用时，都从redis中删除当前应用的权限相关数据
        String authoritiesUrlKeyPrefix = RedisKeys.URL_AUTHORITIES + pathPrefix;
        String authenticatedUrlKey = RedisKeys.URL_AUTHENTICATED + pathPrefix;
        String publicUrlKey = RedisKeys.URL_PUBLIC + pathPrefix;

        Set keys = redisTemplate.keys(authoritiesUrlKeyPrefix + "*");
        redisTemplate.delete(keys);
        keys = redisTemplate.keys(authenticatedUrlKey + "*");
        redisTemplate.delete(keys);
        keys = redisTemplate.keys(publicUrlKey + "*");
        redisTemplate.delete(keys);

        for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethods.entrySet()) {
            // 获取一个端点对应的所有url
            Set<String> methodUrls = entry.getKey().getPatternsCondition().getPatterns();
            // 获取一个端点上的StarAuthority注解
            StarAuthority starAuthority = entry.getValue().getMethodAnnotation(StarAuthority.class);
            if (starAuthority != null) {
                methodUrls.stream().distinct().forEach(url -> {
                    String[] authorities = starAuthority.value();
                    for (String authority : authorities) {
                        redisTemplate.opsForSet().add(authoritiesUrlKeyPrefix + url, authority);
                    }
                });
            } else {
                // 获取一个端点上的StarAuthenticated注解
                StarAuthenticated starAuthenticated = entry.getValue().getMethodAnnotation(StarAuthenticated.class);
                if (starAuthenticated != null) {
                    methodUrls.stream().distinct().forEach(url -> {
                        redisTemplate.opsForSet().add(authenticatedUrlKey, url);
                    });
                } else {
                    methodUrls.forEach(methodUrl -> publicUrls.add(methodUrl));
                }
            }
        }
        // 将publicUrls中的path，加入到redis中的白名单
        publicUrls.forEach(url -> {
            redisTemplate.opsForSet().add(publicUrlKey, url);
        });
    }
}
